/*
 * GameObject.h
 *
 *  Created on: 20.01.2012
 *      Author: Kirill Blinov
 *      Copyright: LGPL
 */

#ifndef GAMEOBJECT_H_
#define GAMEOBJECT_H_

#include "../BasicTypes.h"
#include "ShaderTypes.h"
#include "../Dynamic System/CAnimationController.h"

/*
 * Contains information necessary for render a 3D model
 */
struct RENDER_MODEL_INFO
{
	RENDER_MODEL_INFO() : primitiveType(GL_TRIANGLES) {};
	RENDER_MODEL_INFO( const GLuint VertexCount, std::vector<GLushort> Indexes,
			           std::vector<TEXTURE_DESCRIPTOR> Textures ) :
	    vertexCount(VertexCount), indexes(Indexes), textures(Textures),
	    indexType(GL_UNSIGNED_SHORT), primitiveType(GL_TRIANGLES) {};

	GLuint                          vertexCount;
	std::vector<GLushort>           indexes;
	std::vector<TEXTURE_DESCRIPTOR> textures;

	//Offset from the buffer first vertex ( equals sum of vertices in previous models )
	UINT                    offsetInVertexBuffer;

	//usually GL_UNSIGNED_SHORT
	GLenum                  indexType;

	//Type of primitives GL_TRIANGLES for example
	GLenum                  primitiveType;
};

/*
 * Contains array of vertices and texture numbers describing a 3D model
 */
struct PURE_COMPRESSED_MODEL
{
	PURE_COMPRESSED_MODEL( std::vector<STANDART_VERTEX> Vertices, std::vector<GLushort> Indexes ):
		vertices(Vertices), indexes(Indexes){};
	std::vector<STANDART_VERTEX> vertices;
	std::vector<GLushort>        indexes;
	//Reference to structure to save offset in vertex buffer
	RENDER_MODEL_INFO           *renderInfo;
};

class GAME_SUBOBJECT_INSTANCE
{
private:

	//Shader program the object to be rendered
	IShaderProgram    *shaderProgram;

public:
	GAME_SUBOBJECT_INSTANCE(){};
	GAME_SUBOBJECT_INSTANCE( RENDER_MODEL_INFO* RenderInfo, const glm::mat4 Position, IShaderProgram *ShaderProgram ) :
		                     shaderProgram(ShaderProgram), renderInfo(RenderInfo), position(Position),
		                     textureDescriptors( RenderInfo->textures ), enabled(true),
		                     sphereRadius(0)
	{
	}

	~GAME_SUBOBJECT_INSTANCE()
	{
		delete shaderProgram;
	}

	bool UpdateTexture( const TEXTURE_DESCRIPTOR &Texture )
	{
		bool Result = false;

		for ( std::vector<TEXTURE_DESCRIPTOR>::iterator Intit = textureDescriptors.begin(),
			  Intend = textureDescriptors.end();
			  Intit != Intend; ++Intit )
		{
			TEXTURE_DESCRIPTOR *TextureDesc = Intit.base();

			if (( !TextureDesc->alias.empty() && ( TextureDesc->alias == Texture.alias ) ) ||
				( TextureDesc->number == Texture.number ))
			{
				TextureDesc->name = Texture.name;
				if ( Texture.textureGLid > 0 )
				{
					TextureDesc->textureGLid = Texture.textureGLid;
					TextureDesc->textureType = Texture.textureType;
				}
				Result = true;
				break;
			}
		}

		return Result;
	}

	IShaderProgram* GetShaderProgram() const { return shaderProgram; };
	void SetShaderProgram( IShaderProgram* ShaderProgram ) { shaderProgram = ShaderProgram; };

public:

	RENDER_MODEL_INFO              *renderInfo;

	//Position relatively to the center of main object
	glm::mat4                       position;

	std::vector<TEXTURE_DESCRIPTOR> textureDescriptors;

	//Matrixes for bones
	std::vector<glm::mat4>          bonesMatrixes;

	bool                            enabled;

	std::string                     name;

	//Object dimensions sphere
	float sphereRadius;

	// Only one corner is enough
	glm::vec3 dimensionCorner;

	//Matrix with offset from 0 to 3D model
	glm::mat4 correctionMatrix;
};

/*
 * Contains game object ( set of 3D models with its world matrixes and bones )
 */
struct SUBOBJECT_WITH_CONTROLLER
{
private:
	SUBOBJECT_WITH_CONTROLLER(){};

private:

	GAME_SUBOBJECT_INSTANCE *subobject;
	CAnimationController    *controller;

public:

	SUBOBJECT_WITH_CONTROLLER( GAME_SUBOBJECT_INSTANCE *Subobject, CAnimationController* Controller ) :
		subobject(Subobject), controller(Controller)
	{
		if ( ( NULL != Subobject ) && ( NULL != Controller ) )
		{
			controller->SetGameObject( Subobject );
		}
	}
	virtual ~SUBOBJECT_WITH_CONTROLLER()
	{
		if ( NULL != controller )
		{
			delete controller;
		}

		if ( NULL != subobject )
		{
			delete subobject;
		}
	}

	GAME_SUBOBJECT_INSTANCE* GetSubobject(){ return subobject; };
	CAnimationController*    GetAnimationController(){ return controller; };

	void SetSubobject( GAME_SUBOBJECT_INSTANCE* Subobject );
	void SetAnimationController( CAnimationController* AnimationController );

	std::string     GetName() const { return subobject->name; };
};

class GAME_OBJECT
{
friend class CResourceLoader;
private:

	std::vector<SUBOBJECT_WITH_CONTROLLER*> models;

public:
	GAME_OBJECT(): sphereRadius(0.0f), orientation( 1.0f, glm::vec3(0.0f, 0.0f, 0.0f) ), translation(0.0f, 0.0f, 0.0f),
	               worldMatrix(1.0f), correctionMatrix(1.0f), distanceToCamera(0)
	{};

	virtual ~GAME_OBJECT()
	{
		for ( std::vector<SUBOBJECT_WITH_CONTROLLER*>::iterator Begin = models.begin(), End = models.end();
				Begin != End;
				Begin++ )
		{
			SUBOBJECT_WITH_CONTROLLER* TmpObject = *Begin;
			delete TmpObject;
		}
	}

	void Render( SHADER_COMMON_PARAMS &ShaderParams,
			     boost::function<void( RENDER_MODEL_INFO*,
			    		 	 	 	   IShaderProgram*,
			    		               SHADER_COMMON_PARAMS& )> RenderOperator );

	glm::vec3 GetObjectCenterPosition( const bool InWorldSpace = false ) const;

    // Only one corner is enough
	glm::vec3                              dimensionCorner;
	float                                  sphereRadius;
	glm::quat                              orientation;
	glm::vec3                              translation;
	glm::mat4                              worldMatrix;
	//Correction of object position to coordinate center
	glm::mat4                              correctionMatrix;
	float                                  distanceToCamera;
};

#endif /* GAMEOBJECT_H_ */
